package com.apes.scm.voucher.service.cost;

import com.alibaba.fastjson.JSONObject;
import com.apes.framework.api.SpringManager;
import com.apes.framework.plugin.esb.api.SimpleRequest;
import com.apes.framework.util.MapUtil;
import com.apes.pi.service.PiManager;
import com.apes.scm.account.account.model.*;
import com.apes.scm.account.account.repository.CooperationRepository;
import com.apes.scm.account.account.repository.InventoryBatchRepository;
import com.apes.scm.account.account.service.StockService;
import com.apes.scm.account.method.Batch;
import com.apes.scm.account.method.BatchMethod;
import com.apes.scm.account.tax.model.Tax;
import com.apes.scm.account.tax.service.TaxService;
import com.apes.scm.masterdata.frame.model.Dept;
import com.apes.scm.masterdata.frame.model.Store;
import com.apes.scm.masterdata.frame.repository.StoreRepository;
import com.apes.scm.masterdata.party.model.PartyRole;
import com.apes.scm.masterdata.party.service.PartyRoleService;
import com.apes.scm.masterdata.product.model.Product;
import com.apes.scm.masterdata.product.model.ProductUom;
import com.apes.scm.pur.purchaseOrder.service.PurchaseService;
import com.apes.scm.voucher.model.Voucher;
import com.apes.scm.voucher.model.VoucherInventory;
import com.apes.scm.voucher.model.VoucherItem;
import com.apes.scm.voucher.repository.VoucherInventoryRepository;
import com.apes.scm.voucher.repository.VoucherRepository;
import com.apes.scm.voucher.service.BusinessType;
import com.apes.scm.voucher.service.builder.CrossTransferVoucherBuilder;
import lombok.Data;
import org.hibernate.query.internal.NativeQueryImpl;
import org.hibernate.transform.Transformers;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.persistence.EntityManager;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

import static java.util.stream.Collectors.groupingBy;

/**
 * 功能：销售退回成本核算
 *
 * @author xul
 * @create 2018-10-18 9:26
 */
@Component
public class SaleReturnCostMethod extends AbstractCostMethod {

    public SaleReturnCostMethod() {
        this.BusinessTypes = new String[]{BusinessType.SALE_RETURN, BusinessType.CUSTOMER_CONSUME_RETURN_VOUCHER};
    }


    /**
     * 成本核算/价值评估
     *
     * @param voucher
     */
    @Override
    public void costing(Voucher voucher) {
        if (voucher.getInventoryMode().equals(InventoryMode.IM_CONSIGN)) return;
        Collection<VoucherInventory> voucherInventories = costing(voucher, this::costing, this::createInventoryVoExtend);
        supplierConsume(voucher, voucherInventories); /**寄售库存，需要做供应商寄售消耗**/
    }


    /**
     * 功能：按原单批次（原单退货）或创建一个新的自有批次（无源单）退货
     **/
    private void costing(CostContext context) {
        Set<InventoryVo> inventoryVos = new HashSet<>();
        InventoryVo inventoryVo = context.getInventoryVo();

        new BatchMethod<Batch>() {

            private Collection<Batch> wrapBatch(List<VoucherInventory> voucherInventories, ProductUom toUom) {
                return voucherInventories.stream()
                        .filter(voucherInventory -> voucherInventory.getProductQty() - voucherInventory.getQtyReturned() > 0)
                        .map(voucherInventory -> {
                            Batch batch = new Batch();
                            batch.setEntity(voucherInventory);
                            double qty = voucherInventory.getUom().computeQty(voucherInventory.getProductQty() - voucherInventory.getQtyReturned(), toUom, true);
                            batch.setQty(qty);
                            return batch;
                        }).collect(Collectors.toList());
            }

            /**
             * 取采购单价
             */
            private double getPurchasePrice(PartyRole company, PartyRole supplier, Product product) {
                String model = SpringManager.getBean(CrossTransferVoucherBuilder.class).getModel(company);
                String platformCompany = model.equals(CrossTransferVoucherBuilder.BM_EMPOWER) ? "1007" : "1001";
                JSONObject result = invoke("scm.product.getPrice",
                        MapUtil.mapper("HTGSID", platformCompany, "SPID", product.getId(), "TYPE", "saleReturnCost",   // 这里多加一个类型用于获取方法价格区分
                                "GYSID", supplier.getId(), "CGZZID", platformCompany));
                return result.getDoubleValue("purchasePrice");
            }

            /**创建一个新的自有批次（无源单）**/
            private InventoryBatch createInventoryBatch() {
                InventoryBatch inventoryBatch = new InventoryBatch();
                VoucherItem voucherItem = context.getVoucherItem();
                Voucher voucher = voucherItem.getVoucher();
                inventoryBatch.setCompany(voucher.getCompany());
                inventoryBatch.setDept(voucher.getDept());
                PartyRole supplier = SpringManager.getBean(PartyRoleService.class).findOne("A00000", "Supplier");
                if (supplier == null) {
                    throw new RuntimeException("请先定义无单退货供应商：A00000");
                }
                inventoryBatch.setSupplier(supplier);
                Cooperation cooperation = SpringManager.getBean(CooperationRepository.class).findByInventoryModeAndAccountingMethod(InventoryMode.IM_OWN, AccountingMethod.AM_PURCHASE);
                if (cooperation == null) {
                    throw new RuntimeException("请先定义经销合作方式！");
                }
                inventoryBatch.setCooperation(cooperation);
                inventoryBatch.setPurchaseInDate(voucherItem.getVoucher().getCreateDate());
                inventoryBatch.setBatchNumber(voucherItem.getId());
                inventoryBatch.setBatchSerial(0);
                inventoryBatch.setProduct(voucherItem.getProduct());
                Tax tax = SpringManager.getBean(TaxService.class).getPurchaseTax(voucher.getCompany(), supplier, voucherItem.getProduct());
                inventoryBatch.setTax(tax);
                inventoryBatch.setUom(voucherItem.baseUom());
                double taxCostPrice = getPurchasePrice(voucher.getCompany(), supplier, voucherItem.getProduct());
                inventoryBatch.setTaxCostPrice(taxCostPrice);
                inventoryBatch.computeExcludedPriceUnit();

                double qty = Math.abs(voucherItem.computeBaseQty());
                inventoryBatch.setPurchaseQty(qty);
                inventoryBatch.changeSalesQty(qty);
                return inventoryService.save(inventoryBatch);
            }

            @Override
            public Iterable wrap() {
                /**原单明细行**/
                VoucherItem voucherItem = context.getVoucherItem().getVoucherItem();
                if (voucherItem != null) {
                    List<VoucherInventory> voucherInventories = valuationService.findByVoucherItem(voucherItem);
                    return wrapBatch(voucherInventories, inventoryVo.getUom());
                } else if (context.getVoucherItem().getInventoryConsign() != null) { //客户寄售
                    List<InventoryBatch> inventories = new ArrayList<>();
                    inventories.add(context.getVoucherItem().getInventoryConsign().getInventoryBatch());
                    List<Long> batchNumbers = inventories.stream().map(inventoryBatch -> inventoryBatch.getBatchNumber()).collect(Collectors.toList());
                    inventories = inventoryService.findAllByBatchNumber(batchNumbers, inventory -> inventory.getSalesQty());
                    return inventoryService.wrapBatch(inventories, inventoryVo.getUom(), inventory -> inventory.getSalesQty());
                } else {
                    /**无原单退货**/
                    List<InventoryBatch> inventories = new ArrayList<>();
                    inventories.add(createInventoryBatch());
                    return inventoryService.wrapBatch(inventories, inventoryVo.getUom(), inventory -> inventory.getSalesQty());
                }
            }

            @Override
            public void succeed(Batch batch, double increment) {
                Object entity = batch.getEntity();
                if (entity instanceof VoucherInventory) {
                    VoucherInventory voucherInventory = (VoucherInventory) entity;
                    double qty = inventoryVo.getUom().computeQty(increment, voucherInventory.getUom(), true);
                    voucherInventory.incQtyReturned(qty);
                    valuationService.save(voucherInventory);

                    new BatchMethod<Batch>() {
                        @Override
                        public Iterable<Batch> wrap() {
                            List<InventoryBatch> inventories = new ArrayList<>();
                            inventories.add(voucherInventory.getInventoryBatch());
                            List<Long> batchNumbers = inventories.stream().map(inventoryBatch -> inventoryBatch.getBatchNumber()).collect(Collectors.toList());
                            inventories = inventoryService.findAllByBatchNumber(batchNumbers, inventory -> inventory.getSalesQty());
                            return inventoryService.wrapBatch(inventories, inventoryVo.getUom(), inventory -> inventory.getSalesQty());
                        }

                        @Override
                        public void succeed(Batch batch, double increment) {
                            InventoryBatch inventoryBatch = (InventoryBatch) batch.getEntity();
                            addInventoryBatch(inventoryBatch, increment);
                        }

                        @Override
                        public void error(double surplus) {
                            throw new RuntimeException("商品：" + inventoryVo.getProduct().getName() + " 销售数量少 " + surplus + inventoryVo.getUom().getName() + "！");
                        }
                    }.execute(increment);
                } else if (entity instanceof InventoryBatch) {
                    InventoryBatch inventoryBatch = (InventoryBatch) entity;
                    addInventoryBatch(inventoryBatch, increment);
                } else {
                    throw new RuntimeException("成本核算异常！" + entity.getClass().getName());
                }
            }

            private void addInventoryBatch(InventoryBatch inventoryBatch, double increment) {
                Double taxCostPrice = inventoryBatch.getTaxCostPrice();
                taxCostPrice = inventoryBatch.getUom().computePrice(taxCostPrice, inventoryVo.getUom());
                double qty = inventoryVo.getUom().computeQty(increment, inventoryBatch.getUom(), true);
                inventoryBatch.changeBalanceQty(qty);
                inventoryBatch.changeSalesQty(-1 * qty);
                if (context.getVoucherItem().getInventoryConsign() != null) {//客户寄售
                    inventoryService.changeFrostQty(inventoryBatch, inventoryBatch.getUom(), qty, inventoryBatch.getCompany(),
                            inventoryBatch.getInnerBusinessType(), context.getVoucherItem().getVoucher().getId(),
                            String.valueOf(context.getVoucherItem().getId()), "客户寄售消耗退回增加冻结数");
                }
                inventoryBatch = inventoryService.save(inventoryBatch);

                InventoryVo vo = new InventoryVo();
                vo.setOutInventoryBatch(inventoryBatch);
                vo.setQty(increment);
                vo.setTaxCostPrice(taxCostPrice);
                inventoryVos.add(vo);
            }

            @Override
            public void error(double surplus) {
                throw new RuntimeException("商品：["+inventoryVo.getProduct().getId()+"]" + inventoryVo.getProduct().getName() + " 价值库存数量少 " + surplus + inventoryVo.getUom().getName() + "！");
            }
        }.execute(inventoryVo.getQty());
        context.setInventoryVos(inventoryVos);
    }


    private void createInventoryVoExtend(VoucherItem voucherItem, InventoryVo vo) {
        /**指定供应商：第三方销售（使用场景）**/
        if (voucherItem.getSupplier() != null) vo.setSupplier(voucherItem.getSupplier());
    }


    @Autowired
    private EntityManager em;

    @Autowired
    private VoucherRepository voucherRepository;

    @Autowired
    private InventoryBatchRepository inventoryBatchRepository;

    @Autowired
    private VoucherInventoryRepository voucherInventoryRepository;

    /**
     * 重新结转成本（调帐专用）（慎用）
     **/
    public Map recosting(SimpleRequest request) {
        String sql = "select voucher_id, voucher_item_id\n" +
                "  from v_retatil_return_item_costing\n" +
                " where voucher_id is not null\n" +
                "   and voucher_item_id is not null";
        List<OrderItem> orderItems = new ArrayList<>();
        em.createNativeQuery(sql).unwrap(NativeQueryImpl.class)
                .setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP)
                .getResultStream()
                .forEach(m -> {
                    String voucherId = (String) ((Map) m).get("VOUCHER_ID");
                    String voucherItemId = String.valueOf((BigDecimal) ((Map) m).get("VOUCHER_ITEM_ID"));
                    OrderItem orderItem = new OrderItem(voucherId, voucherItemId);
                    orderItems.add(orderItem);
                });
        orderItems.stream().collect(groupingBy(OrderItem::getVoucherId)).forEach((voucherId, items) -> {
            Voucher voucher = voucherRepository.findOne(voucherId);
            if (voucher.getInventoryMode().equals(InventoryMode.IM_CONSIGN)) return;

            List<VoucherInventory> voucherInventories = new ArrayList<>();
            voucher.getVoucherItems().forEach((voucherItem) -> {
                OrderItem orderItem = items.stream().filter(item -> item.getVoucherItemId().equals(String.valueOf(voucherItem.getId()))).findFirst().orElse(null);
                if (orderItem == null) return;

                /**删除原来的价值评估明细**/
                List<VoucherInventory> oldInventories = valuationService.findByVoucherItem(voucherItem);
                voucherInventoryRepository.deleteAll(oldInventories);

                //voucherItem.setInventoryBatch(inventoryBatch);
                InventoryVo vo = createInventoryVo(voucherItem, this::createInventoryVoExtend);
                CostContext context = new CostContext(voucherItem, vo);
                /**价值评估**/
                costing(context);
                context.getInventoryVos().stream().forEach((inventoryVo) -> {
                    InventoryBatch outInventoryBatch = inventoryVo.getOutInventoryBatch();
                    double qty = voucherItem.sign() * inventoryVo.getQty();
                    VoucherInventory voucherInventory = createVoucherInventory(voucherItem, outInventoryBatch, qty);
                    voucherInventories.add(voucherInventory);
                });
            });
            if (!voucherInventories.isEmpty()) {
                SpringManager.getBean(VoucherRepository.class).saveAndFlush(voucher);
                List<VoucherInventory> inventories = valuationService.save(voucherInventories);
                supplierConsume(voucher, inventories); /**寄售库存，需要做供应商寄售消耗**/
            }
        });

        return new HashMap();
    }

    @Data
    class OrderItem {
        private String voucherId;
        private String voucherItemId;

        public OrderItem(String voucherId, String voucherItemId) {
            this.voucherId = voucherId;
            this.voucherItemId = voucherItemId;
        }
    }
}
